package com.fast.common.xml;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XmlUtil {

	protected static final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	protected static final XPathFactory xpf = XPathFactory.newInstance();

	private static final String CHARSET = "UTF-8";

	private static Logger logger = LoggerFactory.getLogger(XmlUtil.class);

	public static String toXml(InputStream input) throws IOException {
		if (input == null) {
			return null;
		}

		byte[] bys = new byte[1024];
		StringBuilder str = new StringBuilder();
		int len = 0;
		while ((len = input.read(bys)) != -1) {
			str.append(new String(bys, 0, len, CHARSET));
		}
		return str.toString();
	}

	public static String toXml(Object input) {
		try {
			return toXmlByAnotation(input);
		} catch (Exception e) {
			logger.error("", e);
		}
		return null;
	}

	public static Map<String, String> parseToMap(String input) throws Exception {
		DocumentBuilder builder = dbf.newDocumentBuilder();
		Document doc = builder.parse(new ByteArrayInputStream(input.getBytes(CHARSET)));
		Element rootEle = doc.getDocumentElement();
		NodeList nodeList = rootEle.getChildNodes();
		if (nodeList == null) {
			throw new IllegalArgumentException("Root Element Has Not Children ");
		}
		Map<String, String> wechat = new HashMap<String, String>();
		int nodeLen = nodeList.getLength();
		for (int index = 0; index < nodeLen; index++) {
			Node node = nodeList.item(index);
			if (node == null) {
				continue;
			}
			if (node.getNodeType() == Node.TEXT_NODE) {
				continue;
			}
			String str = node.getNodeName();
			String val = node.getTextContent();
			if (val == null) {
				val = "";
			}
			logger.info("节点名称==>{}, 节点值==>{}", str, val);
			wechat.put(str, val);
		}
		logger.info("读取到的bean信息==>{}", wechat);
		return wechat;
	}

	public static Map<String, String> parseToMap(InputStream input) throws Exception {
		DocumentBuilder builder = dbf.newDocumentBuilder();
		Document doc = builder.parse(input);
		Element rootEle = doc.getDocumentElement();
		NodeList nodeList = rootEle.getChildNodes();
		if (nodeList == null) {
			throw new IllegalArgumentException("Root Element Has Not Children ");
		}
		Map<String, String> wechat = new HashMap<String, String>();
		int nodeLen = nodeList.getLength();
		for (int index = 0; index < nodeLen; index++) {
			Node node = nodeList.item(index);
			if (node == null) {
				continue;
			}
			String str = node.getNodeName();
			String val = node.getTextContent();
			if (val == null) {
				val = "";
			}
			logger.info("节点名称==>{}, 节点值==>{}", str, val);
			wechat.put(str, val);
		}
		logger.info("读取到的bean信息==>{}", wechat);
		return wechat;
	}

	public static Object toBean(String input, Class<?> clzz) throws Exception {
		return xmlByAnotationToBean(input, clzz);
	}

	public static Object xmlByAnotationToBean(String xml, Class<?> objClz) throws Exception {
		if (xml == null) {
			return null;
		}
		if (objClz == null) {
			return null;
		}
		Object obj = null;
		try {
			obj = objClz.newInstance();
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		if (obj == null) {
			return null;
		}
		Field[] methods = objClz.getDeclaredFields();
		if (methods == null || methods.length <= 0) {
			return null;
		}
		Map<String, Field> cache = new ConcurrentHashMap<String, Field>();
		int len = methods.length;
		for (int index = 0; index < len; index++) {
			Field field = methods[index];
			XmlElement xmlEle = field.getAnnotation(XmlElement.class);
			if (xmlEle == null) {
				continue;
			}
			cache.put(xmlEle.name(), field);
		}
		DocumentBuilder builder = dbf.newDocumentBuilder();
		Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes(CHARSET)));
		Element rootEle = doc.getDocumentElement();
		NodeList nodeList = rootEle.getChildNodes();
		if (nodeList == null) {
			throw new IllegalArgumentException("Root Element Has Not Children ");
		}
		int nodeLen = nodeList.getLength();
		for (int index = 0; index < nodeLen; index++) {
			Node node = nodeList.item(index);
			if (node == null) {
				continue;
			}
			if (node.getNodeType() == Node.TEXT_NODE) {
				continue;
			}
			String str = node.getNodeName();
			Field field = cache.get(str);
			if (field == null) {
				continue;
			}
			String val = node.getTextContent();
			if (val == null) {
				val = "";
			}
			field.setAccessible(true);
			field.set(obj, val);
			logger.debug("节点名称==>{}, 节点值==>{}", str, val);
		}
		return obj;
	}

	public static String beanToXml(Object obj) throws Exception {
		StringBuilder xml = new StringBuilder();
		xml.append("<xml>\n");
		if (obj == null) {
			return xml.toString();
		}
		Class<?> clazz = obj.getClass();
		Method[] methods = clazz.getDeclaredMethods();
		int len = 0;
		if (methods != null && (len = methods.length) > 0) {
			for (int index = 0; index < len; index++) {
				Method mod = methods[index];
				String methodname = mod.getName();
				if (methodname == null) {
					continue;
				}
				boolean isget = methodname.startsWith("get");
				boolean isBool = methodname.startsWith("is");
				if (!isget && !isBool) {
					continue;
				}
				// 只有get或者is的开头的方法才能进入下面的逻辑
				Object objVal = mod.invoke(obj);
				String typname = mod.getReturnType().getName();
				if (Boolean.class.getName().equals(typname)) {
					objVal = false;
				}

				String fieldname = methodname.substring(3);
				xml.append("<");
				xml.append(fieldname);
				xml.append(">");
				if (objVal != null) {
					xml.append(objVal);
				}
				xml.append("</");
				xml.append(fieldname);
				xml.append(">\n");
			}
		}
		xml.append("</xml>");
		return xml.toString();
	}

	public static String toXmlByAnotation(Object obj) throws Exception {
		StringBuilder xml = new StringBuilder();

		if (obj == null) {
			return xml.toString();
		}
		Class<?> clazz = obj.getClass();
		XmlRoot root = clazz.getAnnotation(XmlRoot.class);
		if (root == null) {
			throw new IllegalArgumentException("XmlRoot Element Not Null");
		}
		xml.append("<");
		xml.append(root.name());
		xml.append(">\n");
		Field[] methods = clazz.getDeclaredFields();
		int len = 0;
		if (methods != null && (len = methods.length) > 0) {

		}
		for (int index = 0; index < len; index++) {
			Field field = methods[index];
			XmlElement xmlEle = field.getAnnotation(XmlElement.class);
			if (xmlEle == null) {
				continue;
			}
			String fieldname = field.getName();
			if (fieldname == null) {
				continue;
			}
			Method method = null;
			String methodname = fieldname.substring(0, 1).toUpperCase() + fieldname.substring(1);
			try {
				method = clazz.getMethod("get" + methodname);
			} catch (Exception e) {
			}

			if (method == null) {
				try {
					method = clazz.getMethod("is" + methodname);
				} catch (Exception e) {
				}
			}
			if (method == null) {
				continue;
			}
			// 只有get或者is的开头的方法才能进入下面的逻辑
			Object objVal = method.invoke(obj);
			String typname = method.getReturnType().getName();
			if (Boolean.class.getName().equals(typname)) {
				objVal = false;
			}
			xml.append("<");
			xml.append(xmlEle.name());
			xml.append(">");
			if (objVal != null) {
				if (objVal instanceof List<?>) {
					List<?> list = (List<?>) objVal;
					for (Object inObj : list) {
						xml.append("\n");
						xml.append(toXmlByAnotation(inObj));
						xml.append("\n");
					}
				} else {
					if (NumberUtils.isNumber(objVal.toString())
							|| org.apache.commons.lang3.StringUtils.isAlphanumeric(objVal.toString())) {
						xml.append(objVal);
					} else {
						xml.append("<![CDATA[");
						xml.append(objVal);
						xml.append("]]>");
					}
				}
			}
			xml.append("</");
			xml.append(xmlEle.name());
			xml.append(">\n");
		}
		xml.append("</");
		xml.append(root.name());
		xml.append(">");
		return xml.toString();
	}

}
